#include "cpu/exec.h"
#include "all-instr.h"

static OpcodeEntry load_table [8] = {
  EXW(lds, 1), EXW(lds, 2), EXW(ld, 4), EMPTY , EXW(ld, 1), EXW(ld, 2), EMPTY, EMPTY
};

static make_EHelper(load) {
  decinfo.width = load_table[decinfo.isa.instr.funct3].width;
  idex(pc, &load_table[decinfo.isa.instr.funct3]);
}

static OpcodeEntry store_table [8] = {
  EXW(st, 1), EXW(st, 2), EXW(st, 4), EMPTY, EMPTY, EMPTY, EMPTY, EMPTY
};

static make_EHelper(store) {
  decinfo.width = store_table[decinfo.isa.instr.funct3].width;
  idex(pc, &store_table[decinfo.isa.instr.funct3]);
}


static OpcodeEntry alg_table [8] = {
  EXW(add,4), EXW(sll,4), EXW(slt,4) , EXW(sltu,4), EXW(xor,4), EXW(sr,4), EXW(or,4), EXW(and,4)
};

static OpcodeEntry mul_table [8] = {
  EXW(mul,4), EXW(mulh,4), EXW(mulhsu,4) , EXW(mulhu,4), EXW(div,4), EXW(divu,4), EXW(rem,4), EXW(remu,4)
};

static make_EHelper(alge) {
  decinfo.width = 4;
  OpcodeEntry entry = alg_table[decinfo.isa.instr.funct3];
  if(decinfo.isa.instr.funct7 == 1 && decinfo.isa.instr.opcode6_2 != 4)
    entry = mul_table[decinfo.isa.instr.funct3];
  idex(pc, &entry);
}

static OpcodeEntry br_table [8] = {
  EXW(beq,4), EXW(bne,4) , EMPTY, EMPTY, EXW(blt,4), EXW(bge,4), EXW(bltu,4), EXW(bgeu,4)
};

static make_EHelper(b) {
  decinfo.width = 4;
  idex(pc, &br_table[decinfo.isa.instr.funct3]);
}

static OpcodeEntry csrop_table [8] = {
  EXW(einst,0), EXW(csrrw,4) , EXW(csrrs,4), EXW(csrrc,4), EMPTY, EXW(csrrw,4), EXW(csrrs,4), EXW(csrrc,4)
};

static make_EHelper(csrop) {
  decinfo.width = 4;
  idex(pc, &csrop_table[decinfo.isa.instr.funct3]);
}

static OpcodeEntry opcode_table [32] = {
  /* b00 */ IDEX(ld, load), EMPTY, EMPTY, EMPTY, IDEX(algi, alge), IDEX(U,auipc), EMPTY, EMPTY,// 0 - 7
  /* b01 */ IDEX(st, store), EMPTY, EMPTY, EMPTY, IDEX(alg, alge), IDEX(U, lui), EMPTY, EMPTY, // 8 - 15
  /* b10 */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,//16 - 23
  /* b11 */ IDEX(b,b), IDEX(jalr,jr), EX(nemu_trap), IDEX(jal,j), IDEX(csr,csrop), EMPTY, EMPTY, EMPTY, //24
};

void isa_exec(vaddr_t *pc) {
  decinfo.isa.instr.val = instr_fetch(pc, 4);
  assert(decinfo.isa.instr.opcode1_0 == 0x3);
  idex(pc, &opcode_table[decinfo.isa.instr.opcode6_2]);
}
